package com.demo.utils;

import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class RedisUtils {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 批量添加set
    public void batchAddSetAndExpireAt(String key, final Set<String> list, long unixTimestamp) {
        // 获取key编码方式
        final StringRedisSerializer stringRedisSerializer = (StringRedisSerializer) stringRedisTemplate.getKeySerializer();

        //获取值编码方式
        final RedisSerializer<String> valueSerializer = (RedisSerializer<String>) stringRedisTemplate.getValueSerializer();

        //获取key对应的byte[]
        final byte[] rawKey = stringRedisSerializer.serialize(key);
        stringRedisTemplate.executePipelined((RedisCallback) connection -> {
            for (String str : list) {
                byte[] rawStr = valueSerializer.serialize(str);
                //在set中添加数据
                connection.sAdd(rawKey, rawStr);
            }
            if (unixTimestamp > 0) {
                connection.expireAt(key.getBytes(), unixTimestamp);
            }
            connection.closePipeline();
            return null;
        });
    }

    // 查询set
    public Set<String> getMembersForSet(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }

    // set里添加单个val
    public Long addToSet(String key, String val) {
        return stringRedisTemplate.opsForSet().add(key, val);
    }

    // set里添加单个val
    public void addToSetAndExpireAt(String key, String val, long unixTimestampInMills) {
        stringRedisTemplate.opsForSet().add(key, val);
        stringRedisTemplate.expireAt(key, new Date(unixTimestampInMills));
    }

    public void addExpireAt(String key, long unixTimestampInMills) {
        stringRedisTemplate.expireAt(key, new Date(unixTimestampInMills));
    }

    // 获取集合中的多个随机元素
    public List<String> getRandomElementsFromSet(String key, int count) {
        Set<String> members = stringRedisTemplate.opsForSet().members(key);
        if (members == null || members.isEmpty() || count <= 0) {
            return null;
        }

        List<String> memberList = new ArrayList<>(members);
        Random random = new Random();
        return random.ints(0, memberList.size())
                .distinct()
                .limit(count)
                .mapToObj(memberList::get)
                .collect(Collectors.toList());
    }

    // set里移除单个val
    public Long removeFromSet(String key, String val) {
        return stringRedisTemplate.opsForSet().remove(key, val);
    }

    // 判断是否属于某个set
    public Boolean isMemberOfSet(String key, String val) {
        return stringRedisTemplate.opsForSet().isMember(key, val);
    }

    public Boolean delKey(String key) {
        return stringRedisTemplate.delete(key);
    }

    public Long increment(String key,long delta) {
        return stringRedisTemplate.opsForValue().increment(key,delta);
    }

    public String getFromKey(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public void setForKey(String key,String value,Long expert) {
        stringRedisTemplate.opsForValue().set(key,value,expert, TimeUnit.SECONDS);
    }

    /**
     * redis防重复提交
     *
     * @param key
     * @return
     */
    public boolean getRedisRepetition(String key) {
        //每次访问 + 1 (可以实现计数器)
        Long count = stringRedisTemplate.opsForValue().increment(key, 1);
        if (ObjectUtils.isNotEmpty(count)) {
            if (count == 1) {
                stringRedisTemplate.expire(key, 5, TimeUnit.SECONDS);
                return true;
            }
            return count <= 1;
        }
        return true;
    }

    /**
     * 尝试获取分布式锁
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String requestId, int expireTime) {
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        Boolean result = ops.setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
        return result != null && result;
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean unlock(String lockKey, String requestId) {
        // 确保释放锁的是持有锁的人
        if (requestId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
            return Boolean.TRUE.equals(stringRedisTemplate.delete(lockKey));
        }
        return false;
    }

    public void setForZset(String key,String value,long score) {
        stringRedisTemplate.opsForZSet().add(key,value,score);
    }

    public void removeFromZset(String key, long score, long expert) {
        stringRedisTemplate.opsForZSet().removeRangeByScore(key,0,score - expert);
    }

    public Long countFromZset(String key, long score, long expert) {
        return stringRedisTemplate.opsForZSet().count(key, score - expert, score);
    }

    public void deleteKeys(String prefixKey) {
        Set<String> keys = stringRedisTemplate.keys(prefixKey  + "*");
        // 删除
        if (ObjectUtils.isNotEmpty(keys)) {
            stringRedisTemplate.delete(keys);
        }
    }

    /**
     *  向Redis-Hash中插入单个元素
     * @param redisKey redisKey
     * @param hashKey HashKey
     * @param hashValue HashValue
     */
    public void putToHash(String redisKey, String hashKey, String hashValue, long timeout, TimeUnit timeUnit) {

        stringRedisTemplate.opsForHash().put(redisKey, hashKey, hashValue);
        // 设置键过期时间
        stringRedisTemplate.expire(redisKey, timeout, timeUnit);
    }
    public void putToHashNoTimeout(String redisKey, String hashKey, String hashValue) {

        stringRedisTemplate.opsForHash().put(redisKey, hashKey, hashValue);

    }

    /**
     *  向Redis-Hash中插入所有元素
     * @param redisKey redisKey
     * @param hashMap hashMap
     */
    public void putAllToHash(String redisKey, Map<String, String> hashMap, long timeout, TimeUnit timeUnit) {
        stringRedisTemplate.opsForHash().putAll(redisKey, hashMap);
        stringRedisTemplate.expire(redisKey, timeout, timeUnit);
    }

    /**
     * 从Redis-Hash获取元素
     * @param redisKey redisKey
     * @return entries
     */
    public Map<Object, Object> getFormHash(String redisKey) {
        return stringRedisTemplate.opsForHash().entries(redisKey);
    }

    /**
     * 序列化
     * @param object
     * @return
     */
    public static byte[] serialize(Object object) {
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try {
            //序列化
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            return bytes;
        } catch (Exception e) {

        }
        return null;
    }

    /**
     * 反虚列表
     * @param bytes
     * @return
     */
    public static Object unserialize(byte[] bytes) {
        ByteArrayInputStream bais = null;
        try {
            //反序列化
            bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
        }
        return null;

    }

    public Boolean setIfAbsent(String key, String value, long timeout, TimeUnit timeUnit) {
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, timeUnit);
        return Boolean.TRUE.equals(result);
    }

    /**
     * 删除以指定前缀开头的所有 Redis 键，并返回删除的键数量
     *
     * @param prefix 键的前缀
     * @return 删除的键数量
     */
    public int deleteKeysWithPrefix(String prefix) {
        // 使用 SCAN 命令获取匹配的 key
        ScanOptions scanOptions = ScanOptions.scanOptions().match(prefix + "*").build();
        List<String> keysToDelete = new ArrayList<>();

        // 使用 Redis 连接执行 SCAN 操作
        stringRedisTemplate.execute((RedisCallback<Void>) connection -> {
            // 显式声明 Cursor 类型
            Cursor<byte[]> cursor = connection.scan(scanOptions);
            try {
                while (cursor.hasNext()) {
                    keysToDelete.add(new String(cursor.next()));
                }
            } finally {
                // 确保关闭 Cursor
                try {
                    cursor.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return null; // 返回 null，因为没有实际返回值
        });

        // 批量删除匹配的 key
        if (!keysToDelete.isEmpty()) {
            stringRedisTemplate.delete(keysToDelete);
        }

        // 返回删除的键数量
        return keysToDelete.size();
    }
}